package tk.mybatis.springboot.listener;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import tk.mybatis.springboot.constants.FrequencyStruct;
import tk.mybatis.springboot.constants.RequestLimit;
import tk.mybatis.springboot.model.InterceptRule;
import tk.mybatis.springboot.service.InterceptRuleService;


public class FrequencyHandlerInterceptor extends HandlerInterceptorAdapter {
		@Resource
	    InterceptRuleService interceptRuleService;
		private Logger logger = LoggerFactory.getLogger(FrequencyHandlerInterceptor.class);   
	    private static final int MAX_BASE_STATION_SIZE = 100000;  
	    private static Map<String, FrequencyStruct> BASE_STATION = new HashMap<String, FrequencyStruct>(MAX_BASE_STATION_SIZE);  
	    private static final float SCALE = 0.75F;  
	    private static final int MAX_CLEANUP_COUNT = 3;  
	    private static final int CLEANUP_INTERVAL = 1000;  
	    private Object syncRoot = new Object();  
	    private int cleanupCount = 0;  
	      
	    @Override  
	    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
	    	RequestLimit methodFrequency = ((HandlerMethod) handler).getMethodAnnotation(RequestLimit.class);  
	        RequestLimit classFrequency = ((HandlerMethod) handler).getBean().getClass().getAnnotation(RequestLimit.class);  
	          
	        boolean going = true;  
//	        if(classFrequency != null) {  
//	            going = handleFrequency(request, response, classFrequency);  
//	        }  
	          
	        if(going && methodFrequency != null) {  
	            going = handleFrequency(request, response, methodFrequency);  
	        }  
	        return going;  
	    }  
	  
	    private boolean handleFrequency(HttpServletRequest request, HttpServletResponse response, RequestLimit frequency) {  
	          
	        boolean going = true;  
	        if(frequency == null) {  
	            return going;  
	        }  
	          
	        String name = frequency.name();  
	        int limit = frequency.limit();  
	        int time = frequency.time();  
	          System.out.println(time);
	          System.out.println(limit);
	        if(time == 0 || limit == 0) {  
	            going = false;  
	            response.setStatus(HttpServletResponse.SC_FORBIDDEN);  
	            return going;  
	        }  
	              
	        long currentTimeMilles = System.currentTimeMillis() / 1000;  
	          
	        String ip = getRemoteIp(request);  
	        logger.info("===================访问ip==========:"+ip);
	        System.out.println("===================访问ip==========:"+ip);
	        String key = ip + "_" + name;  
	        FrequencyStruct frequencyStruct = BASE_STATION.get(key);  
	        if(frequencyStruct == null) {  
	              
	            frequencyStruct = new FrequencyStruct();  
	            frequencyStruct.uniqueKey = name;  
	            frequencyStruct.start = frequencyStruct.end = currentTimeMilles;  
	            frequencyStruct.limit = limit;  
	            frequencyStruct.time = time;  
	            frequencyStruct.accessPoints.add(currentTimeMilles);  
	  
	            synchronized (syncRoot) {  
	                BASE_STATION.put(key, frequencyStruct);  
	            }  
	            if(BASE_STATION.size() > MAX_BASE_STATION_SIZE * SCALE) {  
	                cleanup(currentTimeMilles);  
	            }  
	        } else {  
	            frequencyStruct.end = currentTimeMilles;  
	            frequencyStruct.accessPoints.add(currentTimeMilles);  
	        }  
	        //时间是否有效  
	     
	           if(frequencyStruct.end - frequencyStruct.start >= time) { 
	        
	              if(logger.isDebugEnabled()) {  
	                logger.debug("frequency struct be out of date, struct will be reset., struct: {}", frequencyStruct.toString());  
	            }  
	            frequencyStruct.reset(currentTimeMilles);  
	        } else {  
	        	
	            int count = frequencyStruct.accessPoints.size();
	            if(count > limit) { 
	            	   InterceptRule rule=interceptRuleService.seletByIp(ip);
		   	           if(rule!=null){
		   	        	        Date date=new Date();
		   	        		    if(date.getTime()>rule.getEndDate().getTime()){
		   	        		    	interceptRuleService.deltel(ip);
		   	        		    }else{
		   	        		 	if(rule.getStatus()==false){
		   	        		 	 going = false;  
	   	        		    	 response.setStatus(HttpServletResponse.SC_FORBIDDEN); 
			   	        		}
		   	        		}
		   	             }else{
	            	     InterceptRule inter=new InterceptRule();
	            	     inter.setCreateDate(new Date());
	            	     inter.setIp(ip);
	            	     inter.setStatus(false);
	            	     Calendar calendarbi = Calendar.getInstance();
						 calendarbi.setTime(new Date());
						 calendarbi.add(Calendar.HOUR, 24);
						 inter.setEndDate(calendarbi.getTime());
						 interceptRuleService.insert(inter);
	                     if(logger.isDebugEnabled()) {  
	                     logger.debug("key: {} too frequency. count: {}, limit: {}.", key, count, limit);  
	                   }  
	                 going = false;  
	                 response.setStatus(HttpServletResponse.SC_FORBIDDEN);  
	            }  
	           }else{
	        	   InterceptRule rule=interceptRuleService.seletByIp(ip);
	   	           if(rule!=null){
	   	        	        Date date=new Date();
	   	        		    if(date.getTime()>rule.getEndDate().getTime()){
	   	        		    	interceptRuleService.deltel(ip);
	   	        		    }else{
	   	        		 	if(rule.getStatus()==false){
	   	        		 	 going = false;  
	        		    	 response.setStatus(HttpServletResponse.SC_FORBIDDEN); 
		   	        		}
	   	        		}
	   	             }
	           }  
	        }
	        return going;  
	    }  
	  
	    private void cleanup(long currentTimeMilles) {  
	          
	        synchronized (syncRoot) {  
	              
	            Iterator<String> it = BASE_STATION.keySet().iterator();  
	            while(it.hasNext()) {  
	                  
	                String key = it.next();  
	                FrequencyStruct struct = BASE_STATION.get(key);  
	                if((currentTimeMilles - struct.end) > struct.time) {  
	                    it.remove();  
	                }  
	            }  
	              
	            if((MAX_BASE_STATION_SIZE - BASE_STATION.size()) > CLEANUP_INTERVAL) {  
	                cleanupCount = 0;  
	            } else {  
	                cleanupCount++;  
	            }  
	              
	            if(cleanupCount > MAX_CLEANUP_COUNT ) {  
	                randomCleanup(MAX_CLEANUP_COUNT);  
	            }  
	        }  
	    }  
	  
	    /** 
	     * 随机淘汰count个key 
	     *  
	     * @param maxCleanupCount 
	     */  
	    private void randomCleanup(int count) {  
	        //防止调用错误  
	        if(BASE_STATION.size() < MAX_BASE_STATION_SIZE * SCALE) {  
	            return;  
	        }  
	          
	        Iterator<String> it = BASE_STATION.keySet().iterator();  
	        Random random = new Random();  
	        int tempCount = 0;  
	          
	        while(it.hasNext()) {  
	            if(random.nextBoolean()) {  
	                it.remove();  
	                tempCount++;  
	                if(tempCount >= count) {  
	                    break;  
	                }  
	            }  
	        }  
	    }  
	      
	    private String getRemoteIp(HttpServletRequest request) {  
	  
	    	  String ipAddress=request.getHeader("X-Real-IP");
	  		
			   if(ipAddress!=null){
				   return ipAddress;
			   }
			   ipAddress = request.getHeader("x-forwarded-for");  
		            if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {  
		                ipAddress = request.getHeader("Proxy-Client-IP");  
		            }  
		            if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {  
		                ipAddress = request.getHeader("WL-Proxy-Client-IP");  
		            }  
		            if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {  
		                ipAddress = request.getRemoteAddr();  
		                if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){  
		                    //根据网卡取本机配置的IP  
		                    InetAddress inet=null;  
		                    try {  
		                        inet = InetAddress.getLocalHost();  
		                    } catch (UnknownHostException e) {  
		                        e.printStackTrace();  
		                    }  
		                    ipAddress= inet.getHostAddress();  
		                }  
		            }  
		            //对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割  
		            if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15  
		                if(ipAddress.indexOf(",")>0){  
		                    ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));  
		                }  
		            }  
		            return ipAddress;   
	  
	    }  
}
